package icu.lingjie.灵界;

import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import icu.lingjie.灵阵.灵阵类;
import static icu.lingjie.util.ConfigUtil.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.IntConsumer;

@Slf4j
public class 灵界类 {

    private 灵阵类 灵阵;

    /**
     * 文件开头
     * "LING_JIE".split("").map((c,i)=>c.charCodeAt().toString(16)).join("").toUpperCase();
     */
    public static final Long LING_JIE = Long.parseLong("4C494E475F4A4945", 16);

    /**
     * 只能解析版本号是2的
     */
    private static long 版本号 = 2;

    /**
     * 世界时间
     */
    private final AtomicLong 世界时间 = new AtomicLong();
    public long get世界时间() {
        return 世界时间.get();
    }

    /**
     * 每个小格分为八瓣, 分别记录不同方位的粒子；
     *          xyz     左右，前后，上下
     * 例如   7  111
     *    3 ----- 7
     *   /|      /|
     *  1 ------5 |
     *  | |     | |
     *  | |2 ---|-|6
     *  |/      |/
     *  0 ------4
     * 粒子可能大于 8 个，所以有个指针，用链表存储
     *
     * 每个粒子 位置x,y,z  速度vx,vy,vz   其他信息/粒子种类
     * 大小       4,4,4      4, 4, 4          8           共32位
     * 顺组       0,1,2      3, 4, 5          6
     */
    private static class 小格类 {
        // 存储这八个点
        private final int[] 八个点 = new int[8];
        // 指向下一个
        private 小格类 下一个 = null;

        // 清空
        public void clear() {
            for (int i = 0; i < 8; i++) {
                this.八个点[i] = 0;
            }
            this.下一个 = null;
            bitMap = 0;
        }
        // 是否为空
        byte bitMap = 0;
        public boolean isEmpty(){
            return bitMap == 0;
        }
        // 获取
        public void 访问点们(int 位置, IntConsumer consumer) {
            for (小格类 cell = this; cell != null; cell = cell.下一个) {
                if (cell.八个点[位置] != 0) {
                    consumer.accept(cell.八个点[位置]);
                } else {
                    break;
                }
            }
        }
        public void 访问点们(int x, int y, int z, IntConsumer consumer) {
            this.访问点们(get位置(x, y, z), consumer);
        }
        // 放置
        public 小格类 放置点(int 位置, int p){
            for (小格类 cell=this; cell!=null; cell=cell.下一个) {
                cell.bitMap |= (1 << 位置);
                if (cell.八个点[位置] == 0) {
                    cell.八个点[位置] = p;
                    break;
                } else if (cell.下一个 == null) {
                    cell.下一个 = getNew小格();
                }
            }
            return this;
        }
        public 小格类 放置点(int x, int y, int z, int p) {
            return this.放置点(get位置(x, y, z), p);
        }
        // 根据世界坐标获取这个点在小格内的位置
        public static int get位置(int x, int y, int z){
            return ((x&1)<<2) | ((y&1)<<1) | (z&1);
        }
        // 统计点的个数
        public int get点的个数(){
            int sum = 0;
            for (小格类 cell=this; cell!=null; cell=cell.下一个) {
                for (int p : cell.八个点) {
                    if (p != 0) {
                        sum++;
                    }
                }
            }
            return sum;
        }
    }

    /**
     * 使用的哪个灵界数组
     * 灵界数据有两套，方便刷新地图
     */
    private 小格类[][][] 当前的灵界数组;
    private 小格类[][][] 下一个灵界数组;
    public 小格类[][][] get当前的灵界数组(){
        return this.当前的灵界数组;
    }
    private void 世界时间加一并交换世界数组(){
        synchronized (saveFileLock) {
            小格类[][][] tmp = 当前的灵界数组;
            当前的灵界数组 = 下一个灵界数组;
            下一个灵界数组 = tmp;
            // 时间要在最后改变，他的改变可能会放行自旋中的线程
            世界时间.incrementAndGet();
        }
    }
    private static final ThreadLocal<Stack<小格类>> 小格缓存ThreadLocal = new ThreadLocal();
    private static Stack<小格类> get小格子缓存() {
        Stack<小格类> cells = 小格缓存ThreadLocal.get();
        if (cells == null) {
            cells = new Stack();
            小格缓存ThreadLocal.set(cells);
        }
        return cells;
    }
    private static 小格类 getNew小格(){
        Stack<小格类> cells = get小格子缓存();
        小格类 cell;
        if (cells.size() == 0) {
            cell = new 小格类();
        } else {
            cell = cells.pop();
        }
        return cell;
    }
    // 清理小格子，并缓存起来
    private void clearAndCacheOld小格(小格类 cell) {
        if (cell == null) {
            return;
        }
        Stack<小格类> cells = get小格子缓存();
        for (小格类 next; cell!=null; cell=next) {
            next = cell.下一个;
            cell.clear();
            if (cells.size() < maxFreeCellCacheSize) {
                cells.add(cell);
            }
        }
    }

    /**
     * 缓存
     */
    private Cache<Long, PointsView> pvCache;

    /**
     * 初始化灵界
     */
    public volatile boolean 初始化好了吗 = false;
    public void 初始化(灵阵类 灵阵) throws Exception{
        this.灵阵 = 灵阵;
        log.info("初始化灵界数组 灵界长度X={},灵界宽度Y={},灵界高度Z={}",灵界长度X,灵界宽度Y,灵界高度Z);
        this.当前的灵界数组 = new 小格类[灵界长度X>>1][灵界宽度Y>>1][灵界高度Z>>1];
        this.下一个灵界数组 = new 小格类[灵界长度X>>1][灵界宽度Y>>1][灵界高度Z>>1];
        加载地图文件();
        log.info("初始化灵界数组成功 灵界长度X={},灵界宽度Y={},灵界高度Z={}",灵界长度X,灵界宽度Y,灵界高度Z);
        // 缓存前端请求的数据
        pvCache = CacheBuilder.newBuilder()
                .maximumSize(缓存最大条数)
                .expireAfterAccess(缓存最大时间, TimeUnit.SECONDS)
                .build();
        初始化好了吗 = true;
    }

    /**
     * 加载地图文件
     * 1. LING_JIE, 文件开头
     * 2. 地图文件的信息有多少 byte，long
     * 3. 地图文件的信息
     *      版本号， long, 只能解析版本为2的
     *      世界时间, long, 世界时间
     * 4. 三维世界地图
     *      int     int     int     int
     *      x       y       z       p
     */
    @Data
    private static class 地图文件的信息类 {
        Long version;
        Long time;
    }
    private void 加载地图文件() throws Exception{
        log.info("加载地图文件, 灵界地图文件={}", 灵界地图文件);
        File file = new File(灵界地图文件);
        if (!file.exists()) {
            log.error("灵界地图文件不存在!,{}",灵界地图文件);
            if (autoCreateFile) {
                生成一个随机的世界地图();
                保存地图文件();
            } else {
                throw new Exception("灵界地图文件不存在，文件：（"+灵界地图文件+"）");
            }
        }
        try(FileInputStream fileInputStream = new FileInputStream(file);
            DataInputStream in = new DataInputStream(fileInputStream)) {
            // 1. LINGZHEN
            final long head = in.readLong();
            if (head != LING_JIE){
                throw new Exception("文件类型错误，不是灵界地图文件，文件：（"+灵界地图文件+"）");
            }

            // 2. 读取文件信息
            final int 地图文件的信息有多少byte = (int) in.readLong();
            byte [] 地图文件的信息的byte数组 = new byte[地图文件的信息有多少byte];
            in.read(地图文件的信息的byte数组);
            final String 文件信息Str = new String(地图文件的信息的byte数组, StandardCharsets.UTF_8);
            地图文件的信息类 地图信息 = new ObjectMapper().readValue(文件信息Str, 地图文件的信息类.class);

            // 3. 地图信息
            if (地图信息.getVersion() != 版本号) {
                throw new Exception("地图文件版本号为"+ 地图信息.getVersion() +"，解析不了。只能解析版本号为" + 版本号 + "的。文件：（"+灵界地图文件+"）");
            }
            this.世界时间.set(地图信息.getTime());

            // 4. 世界地图
            //    int     int     int     int   共 16 个byte
            //    x       y       z       p
            byte[] bytes = new byte[4096];
            int len, count=0;
            while ((len=in.read(bytes)) > 0) {
                for (int i = 0; i < len; i+=16) {
                    int x = (bytes[i+ 0]<<24) | (bytes[i+ 1]<<16) | (bytes[i+ 2]<<8) | (bytes[i+ 3]);
                    int y = (bytes[i+ 4]<<24) | (bytes[i+ 5]<<16) | (bytes[i+ 6]<<8) | (bytes[i+ 7]);
                    int z = (bytes[i+ 8]<<24) | (bytes[i+ 9]<<16) | (bytes[i+10]<<8) | (bytes[i+11]);
                    int p = (bytes[i+12]<<24) | (bytes[i+13]<<16) | (bytes[i+14]<<8) | (bytes[i+15]);
                    setP(x, y, z, p);
                    count++;
                }
            }
            final 小格类[][][] tmp = this.当前的灵界数组;
            this.当前的灵界数组 = this.下一个灵界数组;
            this.下一个灵界数组 = tmp;
            log.info("加载地图文件成功,灵界地图文件={},世界时间={},点的个数={}",灵界地图文件, 世界时间, count);
        }
    }

    public void saveMap(){
        synchronized (saveFileLock) {
            保存地图文件();
        }
    }
    private static Object saveFileLock = new Object();

    /**
     * 保存地图文件
     * 1. LING_JIE, 文件开头
     * 2. 版本号， long, 只能解析版本为1的
     * 3. 世界时间, long, 世界时间
     *
     * 9. skip 256-8-8-8
     * 10. 三维世界地图
     */
    private void 保存地图文件(){
        log.info("保存地图文件");
        File file = new File(灵界地图文件);
        if (!file.exists()) {
            log.error("灵界地图文件不存在={}",灵界地图文件);
            if (!file.getParentFile().exists()){
                file.getParentFile().mkdirs();
            }
        } else {
            String dateTime = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss").format(System.currentTimeMillis());
            file.renameTo(new File(灵界地图文件+dateTime));
            file = new File(灵界地图文件);
        }
        try(FileOutputStream fileOutputStream = new FileOutputStream(file);
            DataOutputStream out = new DataOutputStream(fileOutputStream)) {
            // 1. LINGZHEN
            out.writeLong(LING_JIE);
            // 2. 地图信息
            final 地图文件的信息类 地图信息 = new 地图文件的信息类();
            地图信息.setTime(get世界时间());
            地图信息.setVersion(版本号);
            final String 地图信息str = JSON.toJSONString(地图信息);
            final byte[] 地图信息bytes = 地图信息str.getBytes(StandardCharsets.UTF_8);
            out.writeLong(地图信息bytes.length);
            out.write(地图信息bytes);
            // 3. 世界地图
            for (int i = 0; i < 灵界长度X; i++) {
                for (int j = 0; j < 灵界宽度Y; j++) {
                    for (int k = 0; k < 灵界高度Z; k++) {
                        final 小格类 cell = get小格(i, j, k);
                        int finalI = i;
                        int finalJ = j;
                        int finalK = k;
                        cell.访问点们(i, j, k, p -> {
                            try {
                                out.writeInt(finalI);
                                out.writeInt(finalJ);
                                out.writeInt(finalK);
                                out.writeInt(p);
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        });
                    }
                }
            }
            log.info("保存地图文件成功,灵界地图文件={}",灵界地图文件);
        } catch (Exception e) {
            log.error("保存地图文件失败,灵界地图文件={},{}",灵界地图文件,e);
        }
    }

    /**
     * 获取运行的方法
     */
    public Runnable[] 获取运行的方法(){
        initBarrier();
        Runnable[] 刷新的方法 = new Runnable[线程数];
        int 每个方法的X长度 = 灵界长度X / 线程数;
        for (int i = 0; i < 刷新的方法.length; i++) {
            刷新的方法[i] = new 刷新地图的方法(i*每个方法的X长度, i*每个方法的X长度+每个方法的X长度);
        }
        return 刷新的方法;
    }
    private void initBarrier(){
        开始前的栅栏 = new CyclicBarrier(线程数, 开始前的任务);
        结束时的栅栏 = new CyclicBarrier(线程数, 结束后的任务);
    }

    /**
     * 刷新地图的具体的几个方法
     */
    private final AtomicInteger 有几个刷新完了 = new AtomicInteger(0);
    private final AtomicInteger 有几个开始刷新了 = new AtomicInteger(0);
    private volatile long 记录刷新耗时的时间戳;
    private volatile int decSpeed = 0;
    private int 粒子的总数;
    CyclicBarrier 开始前的栅栏;
    CyclicBarrier 结束时的栅栏;
    private class 刷新地图的方法 implements Runnable {
        // 要刷新的范围： [minx, maxX)
        private final int minX;
        private final int maxX;
        public 刷新地图的方法(int minX, int maxX){
            this.minX = minX;
            this.maxX = maxX;
        }
        @Override
        public void run() {
            try {
                // 1. 开始前做一些事情
                开始前的栅栏.await();
                // 2. 耗时的刷新
                for (int i = minX + 4; i < maxX; i+=2) {
                    // 不清除临界区的
                    if (i + 2 < maxX - 2) {
                        clear一个面的格子(i + 2);
                    }
                    刷新一个面的格子(i);
                }
                // 3. 结束后
                结束时的栅栏.await();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    private Runnable 开始前的任务 = ()->{
        记录刷新耗时的时间戳 = System.currentTimeMillis();
        decSpeed = get世界时间() % 速度多久减少一次 == 0 ? 1 : 0;
        log.info("刷新灵界地图开始前");
        final long currentTimeMillis = System.currentTimeMillis();
        // 清理分界线附近的点
        int 间隔 = 灵界长度X / 线程数;
        for (int i = 0; i < 灵界长度X; i+=间隔) {
            clear一个面的格子(i - 2);
            clear一个面的格子(i);
            clear一个面的格子(i + 2);
            clear一个面的格子(i + 4);
            // 刷新两个面才能完全隔开
            刷新一个面的格子(i);
            刷新一个面的格子(i + 2);
        }
        log.info("开始前的任务耗时={}",System.currentTimeMillis()-currentTimeMillis);
    };
    private Runnable 结束后的任务 = ()->{
        final long currentTimeMillis = System.currentTimeMillis();
        有几个刷新完了.set(0);
        有几个开始刷新了.set(0);
        世界时间加一并交换世界数组();
        粒子的总数 = getPointNum();
        log.info("刷新灵界地图结束，世界时间={}，耗时={}，粒子总数={}",
                世界时间.get(), System.currentTimeMillis()- 记录刷新耗时的时间戳, 粒子的总数);
        log.info("结束后的任务耗时={}",System.currentTimeMillis() - currentTimeMillis);
    };
    // max(|a|, |b|, |c|)
    private int absMax(int a, int b, int c) {
        a = a<0? -a : a;
        b = b<0? -b : b;
        c = c<0? -c : c;
        a = a>b? a : b;
        return a>c? a : c;
    }
    private int clamp(int v, int limit) {
        return v<-limit? -limit : (v>limit? limit : v);
    }
    private int clamp(int v, int min, int max) {
        return v<min? min : (v>max? max : v);
    }
    private void 刷新一个面的格子(int x) {
        for (int j = 0; j < 灵界宽度Y; j+=2) {
            for (int k = 0; k < 灵界高度Z; k+=2) {
                刷新一个格子(x, j, k);
            }
        }
    }
    // 每个小格    vx,x,    vy,y    vz,z,   其他信息/粒子种类
    // 大小        4,4,     4,4,    4,4           8           共32位
    private void 刷新一个格子(int px, int py, int pz){
        // 获取当前的格子
        小格类 cell = get小格(px, py, pz);
        if (cell.isEmpty()){
            return;
        }
        for (int w = 0; w < 8; w++) {
            int finalW = w;
            cell.访问点们(w, p -> {
                // 世界地图中的点位置
                final int wx = px + ((finalW>>2)&1);
                final int wy = py + ((finalW>>1)&1);
                final int wz = pz + (finalW&1);

                // 点的具体位置 和 速度信息
                final int x=getX(p), y=getY(p), z=getZ(p);
                int vx=getVX(p), vy=getVY(p), vz=getVZ(p);
                final int t=getT(p);

                // 计算合加速度，灵力 + 遍历四周
                final float[] fa = {灵阵.getAX(wx, wy, wz), 灵阵.getAY(wx, wy, wz), 灵阵.getAZ(wx, wy, wz)};
                for (int i = -2; i <= 2; i+=2) {
                    for (int j = -2; j <= 2; j+=2) {
                        for (int k = -2; k <= 2; k+=2) {
                            final 小格类 cell2 = get小格(px + i, py + j, pz + k);
                            // 跳过空气
                            if (cell2.isEmpty()) {
                                continue;
                            }
                            for (int w2 = 0; w2 < 8; w2++) {
                                int finalW2 = w2;
                                int finalI = i;
                                int finalJ = j;
                                int finalK = k;
                                cell2.访问点们(w2, p2 -> {
                                    // 世界地图中的点位置
                                    final int wx2 = wx + finalI + ((finalW2>>2)&1);
                                    final int wy2 = wy + finalJ + ((finalW2>>1)&1);
                                    final int wz2 = wz + finalK + (finalW2 & 1);

                                    int t2=getT(p2);
                                    int x2=getX(p2), y2=getY(p2), z2=getZ(p2);
                                    // 方向向量 vec3(dx,dy,dz) = p2-p1
                                    int dx = ((wx2 - wx) <<4) + x2 - x;
                                    int dy = ((wy2 - wy) <<4) + y2 - y;
                                    int dz = ((wz2 - wz) <<4) + z2 - z;
                                    // 加速度
                                    int 距离 = absMax(dx, dy, dz);
                                    if (距离 >= 64 || 距离 == 0) {
                                        return;
                                    }
                                    float a = (float) fArr[距离] * t2 / t;
                                    Vec3 dir = getFDir(dx, dy, dz);
                                    fa[0] += dx > 0? -(a * dir.getX()) : (a * dir.getX());
                                    fa[1] += dy > 0? -(a * dir.getY()) : (a * dir.getY());
                                    fa[2] += dz > 0? -(a * dir.getZ()) : (a * dir.getZ());
                                });
                            }
                        }
                    }
                }
                int ax = clamp((int) fa[0],4);
                int ay = clamp((int) fa[1],4);
                int az = clamp((int) (fa[2] + (pz>灵界高度Z的一半?-水平面引力:水平面引力)),4);
                灵阵.setA(px, py, pz, ax, ay, az, 0);
                // 速度
                vx = clamp(vx + ax, 6 - decSpeed);
                vy = clamp(vy + ay, 6 - decSpeed);
                vz = clamp(vz + az, 6 - decSpeed);
                // 计算新的位置
                int nx = x + vx;
                int ny = y + vy;
                int nz = z + vz;
                int mx = nx > 0xF ? 1 : (nx<0 ? -1 : 0);
                int my = ny > 0xF ? 1 : (ny<0 ? -1 : 0);
                int mz = nz > 0xF ? 1 : (nz<0 ? -1 : 0);
                // 存储
                final int np = toP(nx - (mx << 4), ny - (my << 4), nz - (mz << 4), vx, vy, vz, t);
                setP(px+mx, py+my, pz+mz, np);
            });
        }
    }
    // 力 = fArr[距离];  距离=max(dx,dy,dz);  1/(r^3)-1/(r^2)
    private static final float[] fArr;
    static {
        fArr = new float[64];
//        for (int i = 0; i < fArr.length; i++) {
//            fArr[i] = 0;
//        }
//        double a=8, b=16, c=64; // [256, 157, 98, 62, 38, 22, 12, 5, 0, -3, -6, -7, -8, -9, -9, -9, -9, -9, -9, -9, -9, -9, -8, -8, -8, -8, -8, -7, -7, -7, -7, -6, -6, -6, -6, -6, -5, -5, -5, -5]
//        double a=12, b=24, c=64; // [256, 185, 134, 98, 72, 53, 38, 27, 18, 12, 7, 3, 0, -2, -4, -6, -7, -8, -8, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -8, -8, -8, -8, -8, -8, -8]
        double a=8, b=16, c=8; // [256, 157, 98, 62, 38, 22, 12, 5, 0, -3, -6, -7, -8, -9, -9, -9, -9, -9, -9, -9, -9, -9, -8, -8, -8, -8, -8, -7, -7, -7, -7, -6, -6, -6, -6, -6, -5, -5, -5, -5]
        for (int i = 0; i < fArr.length; i++) {
            double r = (i-a)/b + 1;
            fArr[i] = (float) (c/(r*r*r) - c/(r*r));
        }

//        // 直接连线
//        // 几个关键点连成线： fArr[0]=256, fArr[6]=8, fArr[10]=-8, fArr[32]=0
//        fArr = new float[32];
//        for (int i = 0; i < 6; i++) {
//            fArr[i] = 256 - 41*i;
//        }
//        for (int i = 6; i < 10; i++) {
//            fArr[i] = 8 - 4*(i-6);
//        }
//        for (int i = 10; i < 32; i++) {
//            fArr[i] = -8 + (i-10)*8/22;
//        }
//        for (int i = 0; i < fArr.length; i++) {
//            fArr[i] /= 256;
//        }
        log.info("fArr={}", fArr);
    }

    @Data
    @AllArgsConstructor
    private static class Vec3{
        private float x;
        private float y;
        private float z;
    }
    // 力方向上的单位向量
    private static final Vec3[][][] fDir;
    static {
        fDir = new Vec3[16][16][16];
        fDir[0][0][0] = new Vec3(0,0,0);
        for (int i = 1; i < 16; i++) {
            fDir[i][0][0] = new Vec3(1,0,0);
        }
        for (int j = 1; j < 16; j++) {
            fDir[0][j][0] = new Vec3(0,1,0);
        }
        for (int k = 1; k < 16; k++) {
            fDir[0][0][k] = new Vec3(0,0,1);
        }
        for (int j = 1; j < 16; j++) {
            for (int k = 1; k < 16; k++) {
                int jjkk = j*j + k*k;
                fDir[0][j][k] = new Vec3(0, (float) j*j/jjkk, (float) k*k/jjkk);
            }
        }
        for (int i = 1; i < 16; i++) {
            for (int k = 1; k < 16; k++) {
                int iikk = i*i + k*k;
                fDir[i][0][k] = new Vec3((float) i*i/iikk, 0, (float) k*k/iikk);
            }
        }
        for (int i = 1; i < 16; i++) {
            for (int j = 1; j < 16; j++) {
                int iijj = i*i + j*j;
                fDir[i][j][0] = new Vec3((float) i*i/iijj, (float) j*j/iijj, 0);
            }
        }
        for (int i = 1; i < 16; i++) {
            for (int j = 1; j < 16; j++) {
                for (int k = 1; k < 16; k++) {
                    int iijjkk = i*i + j*j + k*k;
                    fDir[i][j][k] = new Vec3((float) i*i/iijjkk, (float) j*j/iijjkk, (float) k*k/iijjkk);
                }
            }
        }
        log.info("fDir={}", fDir);
    }
    private Vec3 getFDir(int dx, int dy, int dz) {
        return fDir[(dx<0?-dx:dx)>>2][(dy<0?-dy:dy)>>2][(dz<0?-dz:dz)>>2];
    }

    /**
     * 其他方法
     *      byte 数组避免大小端问题
     * 每个小格    vx,x,    vy,y    vz,z,   其他信息/粒子种类
     * 大小        4,4,     4,4,    4,4           8           共32位
     * 顺组        0,1,     2,3,    4,5           6
     */
    // x,y,z 属于 [0, 15]
    public int getX(int p){
        return (p>>24) & 0xF;
    }
    public int getY(int p){
        return (p>>16) & 0xF;
    }
    public int getZ(int p){
        return (p>> 8) & 0xF;
    }
    // v 属于 [-8, 7]
    public int getVX(int p){
        return (p) >> 28;
    }
    public int getVY(int p){
        return (p<<8) >> 28;
    }
    public int getVZ(int p){
        return (p<<16) >> 28;
    }
    // t 属于 [0, 255]
    public int getT(int p){
        return (p) & 0xFF;
    }
    // vx,x, vy,y, vz,z
    public int toP(int x, int y, int z, int vx, int vy, int vz, int t){
        return (vx<<28) | ((x&0xF)<<24)
                | ((vy&0xF)<<20) | ((y&0xF)<<16)
                | ((vz&0xF)<<12) | ((z&0xF)<<8)
                | (t&0xFF);
    }
    // 获取点的信息
    public 小格类 get小格(int x, int y, int z) {
        x &= 灵界长度X_MASK;
        y &= 灵界宽度Y_MASK;
        z &= 灵界高度Z_MASK;
        小格类 cell = 当前的灵界数组[x >> 1][y >> 1][z >> 1];
        if (cell == null) {
            cell = getNew小格();
            当前的灵界数组[x >> 1][y >> 1][z >> 1] = cell;
        }
        return cell;
    }
    // 获取下一个灵界数组中的小格
    public 小格类 get2小格(int x, int y, int z) {
        x &= 灵界长度X_MASK;
        y &= 灵界宽度Y_MASK;
        z &= 灵界高度Z_MASK;
        小格类 cell = 下一个灵界数组[x >> 1][y >> 1][z >> 1];
        if (cell == null) {
            cell = getNew小格();
            下一个灵界数组[x >> 1][y >> 1][z >> 1] = cell;
        }
        return cell;
    }

    // 放置点
    public void setP(int x, int y, int z, int p){
        x &= 灵界长度X_MASK;
        y &= 灵界宽度Y_MASK;
        z &= 灵界高度Z_MASK;
        get2小格(x, y, z).放置点(x, y, z, p);
    }
    // 清理小格子，只要在同一格子就都清理；不删链表中的第一个，删除以后的
    public void clear格子(int x, int y, int z) {
        final 小格类 cell = get2小格(x, y, z);
        clearAndCacheOld小格(cell.下一个);
        cell.clear();
    }
    // 清除一个面的格子
    public void clear一个面的格子(int x) {
        for (int j = 0; j < 灵界宽度Y; j+=2) {
            for (int k = 0; k < 灵界高度Z; k+=2) {
                clear格子(x, j, k);
            }
        }
    }

    private void 生成一个随机的世界地图(){
        Random random = new Random();
        for (int i = 0; i < 灵界长度X; i++) {
            for (int j = 0; j < 灵界宽度Y; j++) {
                for (int k = 0; k < 灵界高度Z; k++) {
                    if (random.nextInt(32) == 1) {
                        setP(i, j, k, random.nextInt());
                    }
                }
            }
        }
        小格类[][][] tmp = 当前的灵界数组;
        当前的灵界数组 = 下一个灵界数组;
        下一个灵界数组 = tmp;
    }
    // 统计地图中点的数目
    private int getPointNum(){
        int sum = 0;
        for (int i = 0; i < 灵界长度X; i+=2) {
            for (int j = 0; j < 灵界宽度Y; j+=2) {
                for (int k = 0; k < 灵界高度Z; k+=2) {
                    sum += get小格(i, j, k).get点的个数();
                }
            }
        }
        return sum;
    }

    /**
     * 16*16*16 区块的点信息
     */
    private class PointsView{
        // 世界坐标中的区块零点坐标
        private int px, py, pz;
        // 当前区块的时间时间
        private long curTime=-1;
        // x, y, z, t   注：x的前4位是相对区块零点的坐标，后四位是小格里的坐标
        // 8, 8, 8, 8
        private byte[] points;
        public PointsView(int x, int y, int z) {
            this.px = (x >> 4) << 4;
            this.py = (y >> 4) << 4;
            this.pz = (z >> 4) << 4;
        }
        public byte[] getPoints(){
            tryFlush();
            return points;
        }
        // 刷新
        private final int maxTempPointNum = 16 * 16 * 16 * 4;
        private ByteBuffer tempPoints = ByteBuffer.allocateDirect(maxTempPointNum + 64);
        private void tryFlush(){
            if (curTime != get世界时间()) {
                synchronized (this) {
                    if (curTime != get世界时间()) {
                        tempPoints.clear();
                        for (int i = 0; i < 16; i++) {
                            for (int j = 0; j < 16; j++) {
                                for (int k = 0; k < 16; k++) {
                                    int x = px + i;
                                    int y = py + j;
                                    int z = pz + k;
                                    final 小格类 cell = get小格(x, y, z);
                                    cell.访问点们(i, j, k, p -> {
                                        if (tempPoints.position() < maxTempPointNum){
                                            tempPoints.put((byte) ((x<<4) | getX(p)));
                                            tempPoints.put((byte) ((y<<4) | getY(p)));
                                            tempPoints.put((byte) ((z<<4) | getZ(p)));
                                            tempPoints.put((byte) getT(p));
                                        }
                                    });
                                }
                            }
                        }
                        tempPoints.flip();
                        final byte[] newBytes = new byte[tempPoints.limit()];
                        tempPoints.get(newBytes);
                        points = newBytes;
                        curTime = get世界时间();
                    }
                }
            }
        }
    }

    // 获取 x,y,z 为中心，共 80*80*80 范围的点
    public void getPointsForWeb(int x, int y, int z, HttpServletResponse response){
        response.setContentType("application/octet-stream");
        try(ServletOutputStream outputStream = response.getOutputStream()) {
            x = (((x >> 4 << 4) - 32) % 灵界长度X + 灵界长度X) % 灵界长度X;
            y = (((y >> 4 << 4) - 32) % 灵界宽度Y + 灵界宽度Y) % 灵界宽度Y;
            z = (((z >> 4 << 4) - 32) % 灵界高度Z + 灵界高度Z) % 灵界高度Z;
            for (int i = 0; i < 5; i++) {
                for (int j = 0; j < 5; j++) {
                    for (int k = 0; k < 5; k++) {
                        final int px = x + i*16;
                        final int py = y + j*16;
                        final int pz = z + k*16;
                        final PointsView pointView = pvCache.get(getIdx(px, py, pz), ()->new PointsView(px, py, pz));
                        outputStream.write(pointView.getPoints());
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    // 计算idx
    public long getIdx(int x,int y,int z){
        return ((long)(x&灵界长度X_MASK)*Y乘Z + (long)(y&灵界宽度Y_MASK)*灵界高度Z + (z&灵界高度Z_MASK))<<2;
    }

}
